package u.can.i.up;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.ibatis.session.SqlSession;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.ssca.dex.DexParser;
import com.ssca.format.DexMethod;

import u.can.i.up.db.PgSession;
import u.can.i.up.db.postgre.AppInfo;
import u.can.i.up.db.postgre.AppInfoDAO;
import u.can.i.up.db.postgre.AppReferedMethodInfo;
import u.can.i.up.db.postgre.AppReferedMethodInfoDAO;
import u.can.i.up.db.postgre.ReferedMethodInfo;
import u.can.i.up.db.postgre.ReferedMethodInfoDAO;
import u.can.i.up.utils.FtpUtil;
import u.can.i.up.utils.TimeUtil;

public class Main {

	private static Logger logger = LogManager.getLogger();

	private static Properties props = System.getProperties();

	private static String separator = (props.getProperty("os.name").toLowerCase().startsWith("win")) ? "\\" : "/";

	private static String userDir = props.getProperty("user.dir");

	public static void main(String[] args) {
		if (args.length == 0 || args.length == 1) {
			try {
				SqlSession sqlSession = PgSession.getInstance().getSqlSession();
				AppReferedMethodInfoDAO appReferedMethodInfoDAO = sqlSession.getMapper(AppReferedMethodInfoDAO.class);
				int currentAppID = appReferedMethodInfoDAO.getCurrentAppID();
				if (args.length == 0) {
					transfor_refered_method(currentAppID, 100000);
				} else if (args.length == 1) {
					try {
						transfor_refered_method(currentAppID, Integer.parseInt(args[0]));
					} catch (Exception e) {
						reportError(2);
					}
				} else {
					reportError(3);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else if (args.length == 2) {
			try {
				int first = Integer.parseInt(args[0]);
				int two = Integer.parseInt(args[1]);
				transfor_refered_method(first, two);
			} catch (Exception e) {
				reportError(2);
			}
		} else {
			reportError(1);
		}

	}

	private static void reportError(int error) {
		switch (error) {
		case 1:
			System.err.println(
					"Condition 1: No params.\n Automatically set the currentAppID accoring to Table app_refered_method_info and set limits of apps as 100,000 by default.");
			System.err.println(
					"Condition 2: Input one param(integer), and set as the limits of apps. Automatically set the currentAppID accoring to Table app_refered_method_info.");
			System.err.println(
					"Condition 3: Input two param(integer), the first is currentAppID and the second is limits of apps.");
			break;
		case 2:
			System.err.println("The params should be Integer.");
			break;
		case 3:
			System.err.println("The params should be Integer.");
			break;
		default:
		}

	}

	private static void transfor_refered_method(int currentAppID, int app_info_limits) {
		// 0. 初始化.
		List<AppInfo> targetAppInfoList = null;
		String ftp = "192.168.1.184";
		String username = "taseUser";
		String password = "62283748";
		int affectedLines = 0;
		int insertBufferSize = 500;
		SqlSession sqlSession = PgSession.getInstance().getSqlSession();

		// 1. 获取已经分析过的id，从该ID起提取refered_method.
		logger.info("STEP[1][Get Current App ID]: " + TimeUtil.getTimeString());
		try {
			AppReferedMethodInfoDAO appReferedMethodInfoDAO = sqlSession.getMapper(AppReferedMethodInfoDAO.class);
			Integer tmp = appReferedMethodInfoDAO.getCurrentAppID();
			currentAppID = (tmp != null) ? tmp : 0;
			logger.info("Current App ID: " + currentAppID);
		} catch (Exception e) {
			e.printStackTrace();
		}

		// 2.从currentAppID起，从app_info表中取出1000个信息.
		logger.info("STEP[2][Get Target App Info List]: " + TimeUtil.getTimeString());
		try {
			AppInfoDAO appInfoDAO = sqlSession.getMapper(AppInfoDAO.class);
			targetAppInfoList = appInfoDAO.getSpecifiedAppInfoListByAppID(currentAppID, app_info_limits);
			logger.info("Target App Info List Size: " + targetAppInfoList.size());
		} catch (Exception e) {
			e.printStackTrace();
		}

		// 3.下载这1000个应用.
		logger.info("STEP[3][Download Target App]: " + TimeUtil.getTimeString());
		if (targetAppInfoList != null && !targetAppInfoList.isEmpty()) {
			for (int i = 0; i < targetAppInfoList.size(); i++) {
				AppInfo ai = targetAppInfoList.get(i);
				String ftp_path = ai.getPath();
				String locate_path = userDir + separator + "tmp" + separator + ai.getMd5() + ".apk";
				logger.info("STEP[3] Downloading [" + i + "]: " + locate_path);
				File folder = new File(locate_path).getParentFile();
				if (!folder.exists() || !folder.isDirectory()) {
					folder.mkdirs();
				}
				FtpUtil.downloadFileFromFtp(ftp, username, password, ftp_path, locate_path);
				logger.info("STEP[3] [" + i + "] Done.");

				// 4.计算refered_method.
				logger.info("STEP[4] Parsing Dex [" + i + "]: " + locate_path);
				List<DexMethod> referedMethod = null;
				try {
					referedMethod = DexParser.getReferedListFromApk(locate_path);
				} catch (Exception e) {
					e.printStackTrace();
					logger.error("It Seems that file cann't be downloaded. [APPID][" + ai.getId() + "]; [PATH]["
							+ ftp_path + "]");
				}

				// 5. 存入refered_method_info表中.
				logger.info("STEP[5] Inserting Refered Method Into {refered_method_info}...");
				List<ReferedMethodInfo> referedMethodInfoList = new ArrayList<ReferedMethodInfo>();
				try {
					ReferedMethodInfoDAO referedMethodInfoDAO = sqlSession.getMapper(ReferedMethodInfoDAO.class);
					affectedLines = 0;
					if (referedMethod != null && !referedMethod.isEmpty()) {
						for (DexMethod dm : referedMethod) {
							referedMethodInfoList.add(new ReferedMethodInfo(dm));
							if (referedMethodInfoList != null && referedMethodInfoList.size() >= insertBufferSize) {
								affectedLines += referedMethodInfoDAO
										.insertReferedMethodInfoList(referedMethodInfoList);
								sqlSession.commit();
								referedMethodInfoList.clear();
							}
						}
						if (referedMethodInfoList != null && !referedMethodInfoList.isEmpty()) {
							affectedLines += referedMethodInfoDAO.insertReferedMethodInfoList(referedMethodInfoList);
							sqlSession.commit();
						}
					}
					logger.info("STEP[5] Done. Affected Lines: " + affectedLines);
				} catch (Exception e) {
					sqlSession.commit();
					e.printStackTrace();
				}

				// 6. 并且建立关联.
				logger.info("STEP[6] Inserting App-ReferedMethod Into {app_refered_method_info}...");
				List<AppReferedMethodInfo> appReferedMethodInfoList = new ArrayList<AppReferedMethodInfo>();
				try {
					AppReferedMethodInfoDAO appReferedMethodInfoDAO = sqlSession
							.getMapper(AppReferedMethodInfoDAO.class);
					affectedLines = 0;
					if (referedMethod != null && !referedMethod.isEmpty()) {
						for (DexMethod dm : referedMethod) {
							appReferedMethodInfoList.add(new AppReferedMethodInfo(ai.getId(), dm));
							if (appReferedMethodInfoList != null && appReferedMethodInfoList.size() >= insertBufferSize) {
								affectedLines += appReferedMethodInfoDAO
										.insertAppReferedMethodInfoList(appReferedMethodInfoList);
								sqlSession.commit();
								appReferedMethodInfoList.clear();
							}
						}
						if (appReferedMethodInfoList != null && !appReferedMethodInfoList.isEmpty()) {
							affectedLines += appReferedMethodInfoDAO
									.insertAppReferedMethodInfoList(appReferedMethodInfoList);
							sqlSession.commit();
						}
					}
					logger.info("STEP[6] Done. Affected Lines: " + affectedLines);
				} catch (Exception e) {
					sqlSession.commit();
					e.printStackTrace();
				}

				// 7.删除应用.
				new File(locate_path).delete();
				logger.info("STEP[7] File{" + locate_path + "} Removed !");
			}
		}
		
		// 8.关session.
		PgSession.getInstance().sessionClose();

	}

}
